package org.nimang.pdfFill;



import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormAnnotation;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Description 填充PDF表单
 * @Author JustHuman
 * @Time 2021/12/22 14:36
 */
public class FillPdfUtil {
    /**
     * 是否自动匹配字体，默认：否
     */
    private Boolean auto = Boolean.FALSE;

    /**
     * 扩展字体路径，默认: resources/fonts/extend
     */
    private String extendPath = DEFAULT_EXTEND_PATH;

    /**
     * 指定字体，默认：华文楷体（resources/fonts/STKAITI.TTF）
     */
    private PdfFont baseFont = defaultFont();


    // 正则表达式，排除非法字符
    private static final String ILLEGAL_CHARACTERS_REGEX = "[\\\\/:*?\"<>|]";
    private static final String DEFAULT_FONT_PATH = "/fonts/STKAITI.TTF";
    private static final String DEFAULT_EXTEND_PATH = "/fonts/extend/";
    private static final Map<String, String> FONTS_MAP = fontsMap();

    /**
     * 不自动匹配字体，使用默认字体
     */
    public FillPdfUtil() {
    }

    /**
     * 使用指定字体
     * @param baseFont 指定字体
     */
    public FillPdfUtil(PdfFont baseFont) {
        if(baseFont != null){
            this.baseFont = baseFont;
        }
    }

    /**
     * 指定是否自动匹配字体，使用默认字体扩展包路径
     * @param auto 是否自动匹配字体
     */
    public FillPdfUtil(Boolean auto) {
        this.auto = auto;
    }

    /**
     * 使用指定字体，使用自定义字体扩展包路径
     * @param baseFont 指定字体
     * @param extendPath 字体扩展包路径
     */
    public FillPdfUtil(PdfFont baseFont, String extendPath) {
        if(baseFont != null){
            this.baseFont = baseFont;
        }
        if (extendPath != null && !extendPath.isBlank()) {
            this.extendPath = extendPath;
        }
    }

    /**
     * 指定是否自动匹配字体，使用自定义字体扩展包路径
     * @param auto 是否自动匹配字体
     * @param extendPath 字体扩展包路径
     */
    public FillPdfUtil(Boolean auto, String extendPath) {
        this.auto = auto;
        if (extendPath != null && !extendPath.isBlank()) {
            this.extendPath = extendPath;
        }
    }

    public Boolean getAuto() {
        return auto;
    }

    public void setAuto(Boolean auto) {
        this.auto = auto;
    }

    public String getExtendPath() {
        return extendPath;
    }

    public void setExtendPath(String extendPath) {
        this.extendPath = extendPath;
    }

    public PdfFont getBaseFont() {
        return baseFont;
    }

    public void setBaseFont(PdfFont baseFont) {
        this.baseFont = baseFont;
    }


    /**
     * 填充PDF表单
     * @param inputFilePath 输入文件路径
     * @param outputFilePath 输出文件路径
     * @param data 填充数据
     */
    public void fill(String inputFilePath, String outputFilePath, Map<String, String> data) {
        OutputStream os = null;
        try {
            os = Files.newOutputStream(Paths.get(outputFilePath));
        } catch (IOException e) {
            throw new RuntimeException("获取输出流失败：" + e.getMessage());
        }
        fill(inputFilePath, os, data);
    }

    /**
     * 填充PDF表单
     * @param inputFilePath 输入文件路径
     * @param response 输出对象
     * @param data 填充数据
     */
    public void fill(String inputFilePath, HttpServletResponse response, Map<String, String> data) {
        fill(inputFilePath, response, null, data);
    }

    /**
     * 填充PDF表单
     * @param inputFilePath 输入文件路径
     * @param response 输出对象
     * @param fileName 输出文件名
     * @param data 填充数据
     */
    public void fill(String inputFilePath, HttpServletResponse response, String fileName, Map<String, String> data){
        response.setContentType("application/pdf;charset=utf-8");
        fileName = getFileName(inputFilePath, fileName);
        String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
        response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
        OutputStream os = null;
        try {
            os = response.getOutputStream();
        } catch (IOException e) {
            throw new RuntimeException("获取输出流失败：" + e.getMessage());
        }
        fill(inputFilePath, os, data);
    }

    /**
     * 填充PDF表单
     * @param inputFilePath 输入文件路径
     * @param os 输出流
     * @param data 填充数据
     */
    public void fill(String inputFilePath, OutputStream os, Map<String, String> data){
        PdfDocument pdfDoc = null;
        try {
            // 打开源PDF文件
            PdfReader reader = new PdfReader(inputFilePath);
            PdfWriter writer = new PdfWriter(os);
            pdfDoc = new PdfDocument(reader, writer);
            // 获取PDF表单
            PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
            // 如未指定字体，则使用默认字体
            PdfFont font = this.baseFont;
            for (String key : data.keySet()) {
                PdfFormField field = form.getField(key);
                if(field != null){
                    field.setValue(data.get(key));
                    if(this.auto){
                        font = getFont(field.getFont());
                    }
                    field.setFont(font);
                }
            }
            // 设置表单不可编辑
            form.flattenFields();
        } catch (Exception e) {
            throw new RuntimeException("填充PDF表单失败：" + e.getMessage(),e);
        } finally {
            if(pdfDoc != null){
                pdfDoc.close();
            }
        }
    }

    /**
     * 默认字体
     * @return PdfFont
     * @throws IOException
     */
    private PdfFont defaultFont() {
        // 默认华文楷体
        PdfFont baseFont;
        try {
            baseFont = PdfFontFactory.createFont(DEFAULT_FONT_PATH, PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED);
        } catch (Exception e) {
            throw new RuntimeException("无法获取默认字体：" + e.getMessage(),e);
        }
        return baseFont;
    }

    /**
     * 获取字体
     * @param font 字体信息
     * @return PdfFont
     */
    private PdfFont getFont(PdfFont font) {
        String fontName = font.getFontProgram().getFontNames().getFontName();
        String fontKey = FONTS_MAP.get(fontName);
        if(fontKey==null){
            font = defaultFont();
        }else {
            try {
                if(fontKey.toLowerCase().endsWith("ttc")){
                    font = PdfFontFactory.createFont(this.extendPath + fontKey + ",0", PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED);
                }else {
                    font = PdfFontFactory.createFont(this.extendPath + fontKey, PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED);
                }
            } catch (IOException e) {
                font = defaultFont();
                e.printStackTrace();
            }
        }
        return font;
    }

    /**
     * 文件名合法性检测
     * @param fileName 文件名
     * @return boolean
     */
    public static boolean isValidFileName(String fileName) {
        if(fileName == null || fileName.trim().length() == 0){
            return false;
        }
        // 编译正则表达式
        Pattern pattern = Pattern.compile(ILLEGAL_CHARACTERS_REGEX);
        // 创建匹配器
        Matcher matcher = pattern.matcher(fileName);
        // 如果在文件名中找到非法字符，则返回false
        return !matcher.find();
    }

    /**
     * 获取文件名
     * @param inputFileName 输入文件路径
     * @param fileName 文件名
     * @return String
     */
    public static String getFileName(String inputFileName, String fileName) {
        if(fileName == null || fileName.trim().length() == 0){
            if(inputFileName.contains("/")){
                fileName = inputFileName.substring(inputFileName.lastIndexOf("/") + 1);
            }else if(inputFileName.contains("\\")){
                fileName = inputFileName.substring(inputFileName.lastIndexOf("\\") + 1);
            }
        }
        if(!isValidFileName(fileName)){
            fileName = "result.pdf";
        }
        if(!fileName.endsWith(".pdf")){
            fileName += ".pdf";
        }
        return fileName;
    }

    /**
     * 字体映射关系
     * @return
     */
    private static Map<String, String> fontsMap(){
        Map<String, String> map = new HashMap<>();
        // 字体映射关系，可扩展
        // itext字体标注/字体文件名
        map.put("DengXian", "Deng.ttf");//等线
        map.put("DengXian-Light", "Dengl.ttf");//等线Light
        map.put("DengXian,Bold", "Dengb.ttf");//等线粗体
        map.put("FZShuTi", "FZSTK.TTF");//方正舒体
        map.put("FZYaoTi", "FZYTK.TTF");//方正姚体
        map.put("FangSong", "simfang.ttf");//仿宋
        map.put("SimHei", "simhei.ttf");//黑体
        map.put("STCaiyun", "STCAIYUN.TTF");//华文彩云
        map.put("STFangsong", "STFANGSO.TTF");//华文仿宋
        map.put("STHupo", "STHUPO.TTF");//华文琥珀
        map.put("STKaiti", "STKAITI.TTF");//华文楷体
        map.put("STLiti", "STLITI.TTF");//华文隶书
        map.put("STSong", "STSONG.TTF");//华文宋体
        map.put("STXihei", "STXIHEI.TTF");//华文细黑
        map.put("STXinwei", "STXINWEI.TTF");//华文新魏
        map.put("STXingkai", "STXINGKA.TTF");//华文行楷
        map.put("STZhongsong", "STZHONGS.TTF");//华文中宋
        map.put("KaiTi", "simkai.ttf");//楷体
        map.put("LiSu", "SIMLI.TTF");//隶书
        map.put("SimSun", "simsun.ttc");//宋体
        map.put("MicrosoftYaHei", "msyh.ttc");//微软雅黑
        map.put("MicrosoftYaHeiLight", "msyhl.ttc");//微软雅黑Light
        map.put("MicrosoftYaHei,Bold", "msyhbd.ttc");//微软雅黑粗体
        map.put("NSimSun", "simsun.ttc");//新宋体
        map.put("YouYuan", "SIMYOU.TTF");//幼圆
        return map;
    }

}
